package com.alibaba.datax.common.element;

import com.alibaba.datax.common.exception.CommonErrorCode;
import com.alibaba.datax.common.exception.DataXException;
import com.alibaba.datax.common.util.Configuration;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.time.FastDateFormat;

import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.*;

public final class ColumnCast {
	
	private StringCast stringCast;
	private DateCast dateCast;
	private BytesCast bytesCast;
	
	public ColumnCast(final Configuration configuration) {
		this.stringCast=new StringCast(configuration);
		this.dateCast=new DateCast(configuration);
		this.bytesCast=new BytesCast(configuration);

	}



	public  Date string2Date(final StringColumn column)
			throws ParseException {
		return stringCast.asDate(column);
	}
	
	public  Date string2Date(final StringColumn column, String dateFormat)
			throws ParseException {
		return stringCast.asDate(column, dateFormat);
	}

	public  byte[] string2Bytes(final StringColumn column)
			throws UnsupportedEncodingException {
		return stringCast.asBytes(column);
	}

	public  String date2String(final DateColumn column) {
		return dateCast.asString(column);
	}

	public  String bytes2String(final BytesColumn column)
			throws UnsupportedEncodingException {
		return bytesCast.asString(column);
	}
}

class StringCast {
	String datetimeFormat = "yyyy-MM-dd HH:mm:ss";

	String dateFormat = "yyyy-MM-dd";

	String timeFormat = "HH:mm:ss";

	List<String> extraFormats = Collections.emptyList();

	String timeZone = "GMT+8";

	FastDateFormat dateFormatter;

	FastDateFormat timeFormatter;

	FastDateFormat datetimeFormatter;

	TimeZone timeZoner;

	String encoding = "UTF-8";

	public StringCast(final Configuration configuration) {
		this.datetimeFormat = configuration.getString(
				"common.column.datetimeFormat", this.datetimeFormat);
		this.dateFormat = configuration.getString(
				"common.column.dateFormat", this.dateFormat);
		this.timeFormat = configuration.getString(
				"common.column.timeFormat", this.timeFormat);
		this.extraFormats = configuration.getList(
				"common.column.extraFormats", Collections.<String>emptyList(), String.class);

		this.timeZone = configuration.getString("common.column.timeZone",
				this.timeZone);
		this.timeZoner = TimeZone.getTimeZone(this.timeZone);

		this.datetimeFormatter = FastDateFormat.getInstance(
				this.datetimeFormat, this.timeZoner);
		this.dateFormatter = FastDateFormat.getInstance(
				this.dateFormat, this.timeZoner);
		this.timeFormatter = FastDateFormat.getInstance(
				this.timeFormat, this.timeZoner);

		this.encoding = configuration.getString("common.column.encoding",
				this.encoding);
	}

	Date asDate(final StringColumn column) throws ParseException {
		if (null == column.asString()) {
			return null;
		}

		try {
			return this.datetimeFormatter.parse(column.asString());
		} catch (ParseException ignored) {
		}

		try {
			return this.dateFormatter.parse(column.asString());
		} catch (ParseException ignored) {
		}

		ParseException e;
		try {
			return this.timeFormatter.parse(column.asString());
		} catch (ParseException ignored) {
			e = ignored;
		}

		for (String format : this.extraFormats) {
			try{
				return FastDateFormat.getInstance(format, this.timeZoner).parse(column.asString());
			} catch (ParseException ignored){
				e = ignored;
			}
		}
		throw e;
	}
	
	Date asDate(final StringColumn column, String dateFormat) throws ParseException {
		ParseException e;
		try {
			return FastDateFormat.getInstance(dateFormat, this.timeZoner).parse(column.asString());
		} catch (ParseException ignored) {
			e = ignored;
		}
		throw e;
	}

	byte[] asBytes(final StringColumn column)
			throws UnsupportedEncodingException {
		if (null == column.asString()) {
			return null;
		}

		return column.asString().getBytes(this.encoding);
	}
}

/**
 * 后续为了可维护性，可以考虑直接使用 apache 的DateFormatUtils.
 * 
 * 迟南已经修复了该问题，但是为了维护性，还是直接使用apache的内置函数
 */
class DateCast {

	String datetimeFormat = "yyyy-MM-dd HH:mm:ss";

	String dateFormat = "yyyy-MM-dd";

	String timeFormat = "HH:mm:ss";

	String timeZone = "GMT+8";

	TimeZone timeZoner = TimeZone.getTimeZone(this.timeZone);

	public DateCast(final Configuration configuration) {
		this.datetimeFormat = configuration.getString(
				"common.column.datetimeFormat", datetimeFormat);
		this.timeFormat = configuration.getString(
				"common.column.timeFormat", timeFormat);
		this.dateFormat = configuration.getString(
				"common.column.dateFormat", dateFormat);
		this.timeZone = configuration.getString("common.column.timeZone",
				this.timeZone);
		this.timeZoner = TimeZone.getTimeZone(this.timeZone);
		return;
	}

	String asString(final DateColumn column) {
		if (null == column.asDate()) {
			return null;
		}

		switch (column.getSubType()) {
		case DATE:
			return DateFormatUtils.format(column.asDate(), this.dateFormat,
					this.timeZoner);
		case TIME:
			return DateFormatUtils.format(column.asDate(), this.timeFormat,
					this.timeZoner);
		case DATETIME:
			return DateFormatUtils.format(column.asDate(),
					this.datetimeFormat, this.timeZoner);
		default:
			throw DataXException
					.asDataXException(CommonErrorCode.CONVERT_NOT_SUPPORT,
							"时间类型出现不支持类型，目前仅支持DATE/TIME/DATETIME。该类型属于编程错误，请反馈给DataX开发团队 .");
		}
	}
}

class BytesCast {
	String encoding = "utf-8";

	public BytesCast(final Configuration configuration) {
		this.encoding = configuration.getString("common.column.encoding",
				this.encoding);
		return;
	}

	String asString(final BytesColumn column)
			throws UnsupportedEncodingException {
		if (null == column.asBytes()) {
			return null;
		}

		return new String(column.asBytes(), encoding);
	}
}